import { WsMsg, WsMsgType } from "../model";
import { WsMsgCb } from "../utils/ws";

const WS_KEY: string = "_task_ws";
const observerMap = new Map<WsMsgType, Set<WsMsgCb>>();
let pingTimer: number;
let ws: WebSocket;

const retry = () => {
  console.log("will retry in 3s...");
  setTimeout(() => initWs(), 3000);
};

const ping = () => {
  if (pingTimer) clearTimeout(pingTimer);
  pingTimer = setTimeout(() => {
    if (ws.readyState !== ws.OPEN) {
      // 连接出问题，准备重试
      console.error("connection error!");
      return retry();
    }
    ws.send(JSON.stringify({ type: WsMsgType.PING }));
  }, 5000);
};

const handleMsg = (msg: string) => {
  try {
    const { type, payload } = JSON.parse(msg) as WsMsg<any, any>;
    switch (type) {
      case WsMsgType.PING:
        break;
      default:
        const cbSet = observerMap.get(type);
        cbSet?.forEach((cb) => cb(payload));
        break;
    }
  } catch (e) {
    console.warn("ws msg parse error!", e);
  }
  ping();
};

const initWs = () => {
  ws = new WebSocket("ws://localhost:9527");
  ws.addEventListener("open", () => {
    console.log("websocket connected at: 9527.");
    ping();
  });

  ws.addEventListener("message", (res) => {
    handleMsg(res.data);
    ping();
  });

  ws.addEventListener("error", (err) => {
    retry();
  });
};


function defineStore() {
  initWs();

  const on = (eventType: WsMsgType, cb: WsMsgCb) => {
    let cbSet = observerMap.get(eventType);
    if (!cbSet) {
      cbSet = new Set();
      observerMap.set(eventType, cbSet);
    }
    cbSet.add(cb);
    return () => cbSet.delete(cb);
  };
  return { on };
}

export const wsStore = defineStore();
